import 'reflect-metadata'
import { HttpRequest } from './request'
import { AxiosRequestConfig } from 'axios'

/**
 * code status
 */
const enum CodeStatus {
	SUCCESS = '000000'
}

interface HttpDecoratorOptions {
	method: 'get' | 'post' | 'put' | 'delete'
	url: string
	config?: AxiosRequestConfig
}
const enum ParamType {
	PARAMS = 'params',
	RESULT = 'result',
	PATH = 'path',
	PATH_PRAMS = 'pathParams'
}

const enum Method {
	GET = 'get',
	POST = 'post',
	PUT = 'put',
	DLETE = 'delete'
}

/**
 * Request Decorator Factory
 */
const createHttpDecorator = (options: HttpDecoratorOptions) => {
	return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
		const { method, url, config = {} } = options
		const originalMethod = descriptor.value
		descriptor.value = async function (...args: any[]) {
			const paramsIndex = Reflect.getMetadata(ParamType.PARAMS, target, propertyKey)
			const resultIndex = Reflect.getMetadata(ParamType.RESULT, target, propertyKey)
			const pathParams = method === Method.GET ? Reflect.getMetadata(ParamType.PATH_PRAMS, target, propertyKey) || [] : []
			try {
				// Handle Path Parameters (GET-specific)
				let finalUrl = url
				if (method === Method.GET) {
					pathParams?.forEach(({ key }) => {
						finalUrl = finalUrl.replace(`{${key}}`, args?.[0][key])
					})
				}
				const response = await HttpRequest[method](finalUrl, args[paramsIndex], config)
				if (response.code == CodeStatus.SUCCESS) {
					args[resultIndex] = response.data
				}
				return originalMethod.call(this, ...args)
			} catch (error) {
				throw new Error(error.message)
			}
		}
	}
}

/**
 * Request Method Decorator
 */
export const Get = (params: any) => createHttpDecorator({ ...params, method: Method.GET })
export const Post = (params: any) => createHttpDecorator({ ...params, method: Method.POST })
export const Put = (params: any) => createHttpDecorator({ ...params, method: Method.PUT })
export const Delete = (params: any) => createHttpDecorator({ ...params, method: Method.DLETE })

/**
 * Parameter Decorator Factory
 */
class ParamsDecoratorClass {
	map: Map<any, any>
	options: any = null
	constructor(options?: any) {
		this.options = options
		this.map = (() => {
			const map = new Map([])
			map.set(ParamType.PARAMS, () => this.PARAMS(this.options))
			map.set(ParamType.RESULT, () => this.RESULT(this.options))
			map.set(ParamType.PATH, () => this.PATH(this.options))
			return map
		})()
	}
	private TYPE(options: any, type: ParamType) {
		const { target, propertyKey, paramIndex, pathKeys = [] } = options
		if (type == ParamType.PATH_PRAMS) {
			const pathParams = Reflect.getMetadata(ParamType.PATH_PRAMS, target, propertyKey) || []
			pathKeys?.forEach((item, sort) => {
				pathParams.push({ key: item, index: sort })
			})
			return Reflect.defineMetadata(type, pathParams, target, propertyKey)
		}
		return Reflect.defineMetadata(type, paramIndex, target, propertyKey)
	}
	private PARAMS(options: any) {
		this.TYPE(options, ParamType.PARAMS)
	}
	private RESULT(options: any) {
		this.TYPE(options, ParamType.RESULT)
	}
	private PATH(options: any) {
		this.TYPE(options, ParamType.PATH_PRAMS)
	}
	public getType(type: ParamType) {
		const func = this.map.get(type)
		return func?.()
	}
}
/**
 * Create a parameter decorator function
 */
const createParamsDecorator = (param: { type: ParamType; pathKeys?: Array<any> }) => {
	return function (target: Object, propertyKey: string, paramIndex: number) {
		const { type, pathKeys } = param
		if (type != ParamType.PATH) {
			return new ParamsDecoratorClass({ target, propertyKey, paramIndex }).getType(type)
		}
		return new ParamsDecoratorClass({ target, propertyKey, pathKeys }).getType(type)
	}
}

/**
 * Parameter Decorator
 */

/**
 * Usage: @Params() req:any (Request Parameter Decorator)
 */
export const Params = () => createParamsDecorator({ type: ParamType.PARAMS })

/**
 * Usage: @Result()  res:any (Response Parameter Decorator)
 */
export const Result = () => createParamsDecorator({ type: ParamType.RESULT })

/**
 * Usage: @PathParam(['id','userId']) _req:any
 * Example Path: url: '/api/demo/{id}/{userId}'
 * Explanation: @PathParam is used to bind URL path parameters in GET requests.
 */
export const PathParam = (key?: Array<any>) => createParamsDecorator({ pathKeys: key, type: ParamType.PATH })
